#include "stdafx.hpp"
#include "ColligereApp.hpp"
#include "ColligereConfigHandler.hpp"
#include "ColligereVersion.hpp"
#include "ColligereLanguageSelectionDlg.hpp"

IMPLEMENT_APP ( ColligereApp )

ColligereApp::ColligereApp() : mpFrame(0), mpSingleChecker(0)
{
	SetVendorName ( "Colligere" );
	SetAppName ( "Colligere" );
}

ColligereApp::~ColligereApp()
{

}

bool ColligereApp::ExitApp()
{
	using namespace boost;
	using namespace col;

	wxInt32 answ = wxYES;

	if ( ConfigHandler::Get().IsEnabled ( ConfigVariable::AskBeforeExiting ) )
	{
		answ = wxMessageBox ( _("Exit Colligere?"), _("Exit?"), 
			wxICON_QUESTION | wxYES_NO, mpFrame );
	}
	if ( answ == wxNO || !mpFrame->Exit() )  
	{
		return false;
	}
	mpFrame->Hide();
	mpFrame->Destroy();
	return true;
}

const wxString ColligereApp::GetVersioString() const
{
#ifdef __WXDEBUG__
	return boost::str ( boost::format ( "%1% v%2% DEBUG" ) % 
		GetAppName() % col::VERSION_NUMBER );
#else
	return boost::str ( boost::format ( "%1% v%2%" ) % 
		GetAppName() % col::VERSION_NUMBER );
#endif
}

bool ColligereApp::OnInit()
{
	using namespace boost;
	using namespace std;
	using namespace col;

	wxStopWatch sw;

	wxSystemOptions::SetOption ( "msw.remap", 
		( GetComCtl32Version() >= 600 && ::wxDisplayDepth() >= 32 ) ? 2 : 0 );

	// The dirs must be valid before parsing the command line.
	InitializeDirectories();

	// PNG support is needed for the toolbar/menu icons.
	wxImage::AddHandler ( new wxPNGHandler() );
	wxXmlResource::Get()->InitAllHandlers();

	LoadIcons();

	try
	{
		LoadResources();
	}
	catch ( std::exception & ) { return false; }

	ConfigHandler &cfgh = ConfigHandler::Get();

	if ( !cfgh.Exists ( ConfigVariable::Language ) )
	{
		LanguageSelectionDlg dlg ( NULL );
		dlg.ShowModal();
		cfgh.SetValue ( ConfigVariable::Language, dlg.GetLanguage() );
	}

	long lang = -1;
	cfgh.GetValue ( ConfigVariable::Language, lang );
	wxASSERT ( lang != -1 );
	mLocale.Init ( lang );
	wxLocale::AddCatalogLookupPathPrefix ( GetLanguageDir() );
	mLocale.AddCatalog("fi");

	if ( !mTempDir.DirExists() )
	{
		mTempDir.Mkdir();
	}

	// This does the actual command line parsing.
	if ( !wxApp::OnInit() ) return false;

	mpSingleChecker = new wxSingleInstanceChecker ( str ( format  
		( "%1%-%2%") % GetAppName() % wxGetUserId() ) );

	if ( mpSingleChecker->IsAnotherRunning() )
	{
		if ( wxMessageBox ( _( "There is already an existing instance "\
			"of the application running. Running multiple instances at the "\
			"same time is untested and might not work. Would you still like "\
			"to continue?" ), _("Continue?"), 
			wxICON_WARNING | wxYES_NO, NULL ) != wxYES )
		{
			return false;
		}
	}
	try
	{	
		mpFrame = new col::MainFrame;

		mpFrame->Show();
		SetTopWindow ( mpFrame );

		Connect ( wxID_ANY, wxEVT_CHAR, wxKeyEventHandler ( ColligereApp::OnChar ) );

		if ( wxFileExists ( mCmdLineInputFile ) )
		{
			mpFrame->OpenDatabase ( mCmdLineInputFile );
		}
		else 
		{
			const wxString default_db ( cfgh.GetValue ( ConfigVariable::DefaultDatabase ) );
			if ( wxFileExists ( default_db ) )
			{
				mpFrame->OpenDatabase ( default_db );
			}
			else if ( !default_db.empty() )
			{
				wxLogStatus ( _("Default Database Not Found") );
			}
		}
		wxLogGeneric ( col::LogPerformance, "Application started in %ldms", sw.Time() );
		// Enter the main loop.
		return true;
	}
	catch ( std::exception &rE )
	{
		wxLogError ( rE.what() );
	}
	catch ( ... )
	{
		wxLogError ( _("Fatal Error"), _("An unhandled exception has occured while "\
			"initializing the application. The application will be terminated.") );
	}
	// Returning false will terminate the app.
	return false;
}

col::MainFrame *ColligereApp::GetFrame()
{
	return mpFrame;
}

void ColligereApp::LoadIcons()
{
	wxFileName icons = mImageDir;
	typedef IconIndex ii;

	LoadIcon ( ii::Vinyl, icons.GetPath 
		( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME ) + "vinyl.png" );
	
	icons.AppendDir ( "Nuvola" );
	icons.AppendDir ( "16x16" );

	const wxString idir ( icons.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME ) );

	LoadIcon ( ii::Cd, idir + "cd.png" );
	LoadIcon ( ii::Configure, idir + "configure.png" );
	LoadIcon ( ii::EditAdd, idir + "editadd.png" );
	LoadIcon ( ii::EditCopy, idir + "editcopy.png" );
	LoadIcon ( ii::EditPaste, idir + "editpaste.png" );
	LoadIcon ( ii::EditRemove, idir + "editremove.png" );
	LoadIcon ( ii::EditUpdate, idir + "kget.png" );
	LoadIcon ( ii::Exit, idir + "exit.png" );
	LoadIcon ( ii::Search, idir + "find.png" );
	LoadIcon ( ii::FileClose, idir + "fileclose.png" );
	LoadIcon ( ii::FileNew, idir + "filenew.png" );
	LoadIcon ( ii::FileOpen, idir + "fileopen.png" );
	LoadIcon ( ii::FileSave, idir + "filesave.png" );
	LoadIcon ( ii::FileSaveAs, idir + "filesaveas.png" );
	LoadIcon ( ii::FolderClosed, idir + "folderblue.png" );
	LoadIcon ( ii::FolderOpened, idir + "folderblueopen.png" );
	LoadIcon ( ii::Help, idir + "help.png" );
	LoadIcon ( ii::Log, idir + "log.png" );
	LoadIcon ( ii::TrashcanEmpty, idir + "trashcanempty.png" );
	LoadIcon ( ii::TrashcanFull, idir + "trashcanfull.png" );

	wxFileName flags = mImageDir;
	flags.AppendDir ( "Flags" );
	const wxString fdir ( flags.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME ) );

	LoadIcon ( ii::FlagUs, fdir + "us.png" );
	LoadIcon ( ii::FlagFi, fdir + "fi.png" );
}

void ColligereApp::LoadIcon ( wxUint32 idx, const wxString &rPath )
{
	mIcons[idx] = wxBitmap ( rPath, wxBITMAP_TYPE_PNG );
}

wxBitmap ColligereApp::GetIcon ( wxUint32 index ) const 
{
	IconMap::const_iterator it = mIcons.find ( index );
	wxASSERT ( it != mIcons.end() );
	return it != mIcons.end() ? it->second : wxNullBitmap;
}

int ColligereApp::OnRun()
{
	try
	{
		if ( m_exitOnFrameDelete == Later )
		{
			m_exitOnFrameDelete = Yes;
		}
		return MainLoop();
	}
	catch ( std::exception &rErr )
	{
		wxLogError ( rErr.what() );
	}
	catch ( ... )
	{
		wxLogError ( _("Fatal Error"), _("An unhandled exception has occured. "\
			"The application will be terminated.") );
	}
	return -1;
}

void ColligereApp::OnUnhandledException()
{
	throw;
}

wxInt32 ColligereApp::OnExit()
{
	delete mpSingleChecker;
	delete wxLog::SetActiveTarget(0);
	return wxApp::OnExit();
}

void ColligereApp::OnChar ( wxKeyEvent &rEvent )
{
	if ( rEvent.GetKeyCode() ==  WXK_TAB ) 
	{
        wxNavigationKeyEvent event_nav;
        event_nav.SetDirection ( !rEvent.ShiftDown() );
        event_nav.SetWindowChange ( rEvent.ControlDown() );
        event_nav.SetEventObject ( rEvent.GetEventObject() );
        
		wxWindow *wnd = wxDynamicCast ( rEvent.GetEventObject(), wxWindow );
        
		for ( wnd = wnd->GetParent(); wnd; wnd = wnd->GetParent() ) 
		{
			if ( wnd->GetEventHandler()->ProcessEvent ( event_nav ) )
			{
				return;
			}
		}
	}
	rEvent.Skip();
}

void ColligereApp::OnInitCmdLine ( wxCmdLineParser &rParser )
{
	static const wxCmdLineEntryDesc cmd_line_desc[] =
	{
		{ wxCMD_LINE_SWITCH, "h", "help", "Shows this help message and exits." },
		{ wxCMD_LINE_SWITCH, "r", "resetcfg", "Resets configuration values to their defaults." },
		{ wxCMD_LINE_SWITCH, "d", "deletecfg", "Deletes all configuration values and exits the application. "\
		"Use only if you know what you're doing." },
		{ wxCMD_LINE_OPTION, "i", "input",  "Input database" },
		{ wxCMD_LINE_SWITCH, "n", "nostart", "Exits the application after parsing the command line." },
		{ wxCMD_LINE_SWITCH, "t", "trackperf", "Enable simple performance tracking." },
		{ wxCMD_LINE_NONE, NULL, NULL, NULL, wxCMD_LINE_VAL_NONE, 0 }, 
	};
	rParser.SetDesc ( cmd_line_desc );
}

bool ColligereApp::OnCmdLineParsed ( wxCmdLineParser &rParser )
{
	using namespace col;

	if ( rParser.Found ( "h") )
	{
		rParser.Usage();
		return false;
	}
	if ( rParser.Found ( "d") )
	{
		ConfigHandler::Get().DeleteAll();
		return false;
	}
	if ( rParser.Found ( "r") )
	{
		ConfigHandler::Get().WriteDefaults ( true );
	}
	if ( !rParser.Found ( "t") )
	{
		wxLog::SetLogLevel ( LogPerformance - 1 );
	}
	
	// Store the possible input database so it can be opened in OnInit().
	rParser.Found ( "i", &mCmdLineInputFile );
	
	return !rParser.Found ( "n" );
}

void ColligereApp::LoadResources() const
{
	wxArrayString xrcs;
	wxDir::GetAllFiles ( GetXrcDir(), &xrcs, "*.xrc" );

	for ( wxUint32 i = 0; i < xrcs.GetCount(); ++i )
	{
		wxLogDebug ( "Loading \"%s\".", xrcs[i] );

		if ( !wxXmlResource::Get()->Load ( xrcs[i] ) )
		{
			throw std::exception();
		}
	}
}

const wxString ColligereApp::GetAppDir() const
{
	return mAppDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

const wxString ColligereApp::GetDataDir() const
{
	return mDataDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

const wxString ColligereApp::GetImageDir() const
{
	return mImageDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

const wxString ColligereApp::GetXrcDir() const
{
	return mXrcDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

const wxString ColligereApp::GetTempDir() const
{
	return mTempDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

const wxString ColligereApp::GetLanguageDir() const
{
	return mLangDir.GetPath ( wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME );
}

void ColligereApp::InitializeDirectories()
{
	mAppDir = wxFileName ( wxStandardPaths::Get().GetExecutablePath() ).GetPath 
		( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
	
	mDataDir = mAppDir;
	mDataDir.AppendDir ( "Data" );
	mDataDir.MakeRelativeTo();

	mTempDir = mAppDir;
	mTempDir.AppendDir ( "Data" );
	mTempDir.AppendDir ( "Temp" );
	mDataDir.Normalize();
	
	mImageDir = mDataDir;
	mImageDir.AppendDir ( "Images" );
	mImageDir.MakeRelativeTo();
	
	mXrcDir = mDataDir;
	mXrcDir.AppendDir ( "XRC" );
	mXrcDir.MakeRelativeTo();

	mLangDir = mDataDir;
	mLangDir.AppendDir ( "Languages" );
	mLangDir.MakeRelativeTo();
}